use super::ffi::{
    osMessageQueueDelete, osMessageQueueGet, osMessageQueueId_t, osMessageQueueNew,
    osMessageQueuePut, osStatus_t_osErrorTimeout, osStatus_t_osOK,
};
use crate::arc::Arc;
use crate::infoln;
use crate::{Error, Result};
use alloc::boxed::Box;
use alloc::string::String;
use core::ffi::c_void;
use core::marker::PhantomData;
use core::ptr::{null, null_mut};

pub struct SyncSender<T> {
    channel: Arc<Channel<T>>,
}

impl<T> Clone for SyncSender<T> {
    fn clone(&self) -> Self {
        Self {
            channel: self.channel.clone(),
        }
    }
}

impl<T> SyncSender<T> {
    pub fn send(&self, data: Box<T>) -> Result<()> {
        self.channel.send(data)
    }
}

pub struct Receiver<T> {
    channel: Arc<Channel<T>>,
}

impl<T> Receiver<T> {
    pub fn try_recv(&self) -> Result<Box<T>> {
        self.channel.recv(0)
    }

    pub fn recv(&self, to: u32) -> Result<Box<T>> {
        self.channel.recv(to)
    }
}

#[repr(C)]
struct ChannelData<T> {
    //boxed data
    data: *mut T,
}

struct Channel<T> {
    queue_id: osMessageQueueId_t,
    phantom: PhantomData<T>,
}

impl<T> Channel<T> {
    pub fn send(&self, data: Box<T>) -> Result<()> {
        let p = Box::into_raw(data);
        infoln!("send data {:p}", p);
        let w = ChannelData { data: p };

        let ret = unsafe {
            osMessageQueuePut(
                self.queue_id,
                &w as *const ChannelData<T> as *const c_void,
                0,
                0,
            )
        };
        if ret != osStatus_t_osOK {
            let p1 = unsafe { Box::from_raw(p) };
            drop(p1);
            return Err(Error::Io(ret as i32, String::from("put queue failed")));
        }
        Ok(())
    }

    fn recv(&self, timeout: u32) -> Result<Box<T>> {
        let mut w = ChannelData { data: null_mut() };
        let mut prio = 0u8;
        let ret = unsafe {
            osMessageQueueGet(
                self.queue_id,
                &mut w as *mut ChannelData<T> as *mut ::core::ffi::c_void,
                &mut prio,
                timeout,
            )
        };
        if ret == osStatus_t_osOK {
            infoln!("recv data {:p}", w.data);
            let d1 = unsafe { Box::from_raw(w.data) };
            Ok(d1)
        } else if ret == osStatus_t_osErrorTimeout {
            Err(Error::Timeout)
        } else {
            Err(Error::Io(ret as i32, String::from("get queue failed")))
        }
    }
}
impl<T> Clone for Channel<T> {
    fn clone(&self) -> Self {
        Self {
            queue_id: self.queue_id,
            phantom: PhantomData,
        }
    }
}

impl<T> Drop for Channel<T> {
    fn drop(&mut self) {
        loop {
            match self.recv(0) {
                Ok(data) => {
                    infoln!("got left data in queue");
                    drop(data)
                }
                Err(_e) => break,
            }
        }
        infoln!("delete queue {:p}", self.queue_id);
        unsafe { osMessageQueueDelete(self.queue_id) };
    }
}

unsafe impl<T> Send for Channel<T> {}
unsafe impl<T> Sync for Channel<T> {}

pub fn sync_channel<T>(size: usize) -> (SyncSender<T>, Receiver<T>) {
    let msg_len = core::mem::size_of::<ChannelData<T>>();
    let queue_size = msg_len * size;
    let queue_id = unsafe { osMessageQueueNew(size as u32, queue_size as u32, null()) };
    infoln!(
        "queue_id {:p} elment {} total size {}",
        queue_id,
        size,
        queue_size
    );
    let chn = Arc::new(Channel {
        queue_id,
        phantom: PhantomData,
    });
    (
        SyncSender {
            channel: chn.clone(),
        },
        Receiver {
            channel: chn.clone(),
        },
    )
}
